﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ModbusLib.Base;
using ProtocalLib.Base;

namespace ModbusLib
{
    public class ModbusRTU : ModbusSerial
    {
        public ModbusRTU() : this("COM1")
        { }
        public ModbusRTU(string portName) : this(portName,9600,Parity.None,8,StopBits.One)
        {
        }
        public ModbusRTU(string portName,int baudRate, Parity parity,int databits,StopBits stopBits)
        {
            PortName = portName;
            BaudRate = baudRate;
            Parity = parity;
            DataBits = databits;
            StopBits = stopBits;
        }

        public override byte[] Read(byte slave, FunctionType func, ushort start, ushort count, Action<object> callback = null)
        {
            List<byte> datas = GetReadBytes(slave,(byte)func,start,count).ToList();
            byte[] crc = CRC16(datas.ToList());
            datas.AddRange(crc.Reverse());
            callback?.Invoke(new LogMessage { MsgType = "Send",Data = datas.ToArray() });

            // 适用于03，04功能码
            int len = count * 2 + 5;
            if (func == FunctionType.RCoilStatus || func == FunctionType.RInputCoil)
                len = (int)Math.Ceiling(count * 1.0 / 8) + 5;
            byte[] respBytes = this.SendAndReceive(datas.ToArray(), len);

            // record log
            if(callback != null)
            {
                LogMessage logMessage = new LogMessage { MsgType = "Recv", Data = respBytes };
                if (respBytes[1] > 0x80)
                {
                    logMessage.ErrorMsg = ModbusBase.Errors[respBytes[2]];
                }
                callback.Invoke(logMessage);
            }
            
            respBytes = this.Check(respBytes, len);
            return respBytes;
        }

        public override void ReadAsync(byte slave, FunctionType func, ushort start, ushort count, int handler, Action<byte[], int, Exception> callback)
        {
            List<byte> datas = GetReadBytes(slave, (byte)func, start, count).ToList();
            byte[] crc = CRC16(datas.ToList());
            datas.AddRange(crc.Reverse());

            // 适用于03，04功能码
            int len = count * 2 + 5;
            // 适用于01，02功能码
            if (func == FunctionType.RCoilStatus || func == FunctionType.RInputCoil)
                len = (int)Math.Ceiling(count * 1.0 / 8) + 5;

            this.AsyncModels.Insert(0, new AsyncModel()
            {
                Handler = handler,
                ReqBytes = datas.ToArray(),
                RespLen = len,
                Completed = callback
            });
        }

        public override void Write(byte slave, FunctionType func, ushort start, ushort count, byte[] datas,Action<object> callback = null)
        {
            List<byte> reqData = GetWriteBytes(slave,(byte)func,start,count,datas).ToList();
            byte[] crc = CRC16(reqData);
            reqData.AddRange(crc.Reverse());
            callback?.Invoke(new LogMessage{ MsgType = "Send", Data = reqData.ToArray() });

            int len = 8;
            byte[] resp = this.SendAndReceive(reqData.ToArray(), len);

            // record log
            if (resp != null)
            {
                LogMessage logMessage = new LogMessage { MsgType = "Recv", Data = resp };
                if (resp[1] > 0x80)
                {
                    logMessage.ErrorMsg = ModbusBase.Errors[resp[2]];
                }
                callback.Invoke(logMessage);
            }

            this.Check(resp, len);
        }

        public void WriteCoils(byte slave, ushort startAddr, bool[] datas)
        {
            // 写入数量
            ushort w_len = (ushort)datas.Length;

            //List<byte> vals = new List<byte>();
            //for (int i = 0; i < datas.Length; i++)
            //{
            //    if (i % 8 == 0)
            //        vals.Add(0x00);

            //    byte tmp = (byte)(datas[i] ? 0x01 : 0x00);
            //    vals[vals.Count - 1] |= (byte)(tmp << (i % 8));
            //}
            byte[] vals = GetBytes(datas);
            this.Write(slave, FunctionType.WCoilStatus, startAddr, w_len, vals);
        }

        public override void WriteAsync(byte slave, FunctionType func, ushort start, ushort count, byte[] datas, int handler, Action<byte[], int, Exception> callback)
        {
            List<byte> reqData = GetWriteBytes(slave, (byte)func, start, count, datas).ToList();
            byte[] crc = CRC16(reqData);
            reqData.AddRange(crc.Reverse());

            int len = 8;

            this.AsyncModels.Insert(0, new AsyncModel()
            {
                Handler = handler,
                ReqBytes = reqData.ToArray(),
                RespLen=len,
                Completed = callback
            });
        }

        protected override byte[] Check(byte[] resp, int len)
        {
            if (resp.Length == 5 || resp.Length == len)
            {
                if (resp[1] > 0x80)
                {
                    throw new Exception(ModbusBase.Errors[resp[2]]);
                }
                List<byte> check = resp.ToList().GetRange(0, len - 2).ToList();
                check.AddRange(CRC16(check).Reverse());
                if (!check.SequenceEqual(resp))
                {
                    throw new Exception("数据传输异常，校验码不匹配");
                }
                
                return check.GetRange(3, len - 5).ToArray();
            }
            else
            {
                throw new Exception("通信响应异常");
            }
        }
    }
}
